﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SlimDX;
using SlimDX.Direct3D9;

namespace RaygunDiplomacy.Graphics
{
   public class Camera
   {
      #region Fields

      bool isViewDirty = true;

      bool isProjectionDirty = true;

      bool isEyeAtPosition = true;

      Matrix view;

      Matrix projection;

      int viewWidth;

      int viewHeight;

      Quaternion rotation = Quaternion.Identity;

      Vector3 position = new Vector3(5, 5, 5);

      float viewDistance = new Vector3(5, 5, 5).Length();

      Vector3 eye = new Vector3(5, 5, 5);

      Vector3 lookAt = new Vector3(0, 0, 0);

      Vector3 up = Vector3.UnitY;

      Vector3 forward = Vector3.UnitZ;

      Vector3 left = Vector3.UnitX;

      #endregion

      #region Properties

      public int ViewWidth
      {
         get { return viewWidth; }
         set
         {
            if (viewWidth != value)
            {
               viewWidth = value;
               isProjectionDirty = true;
            }
         }
      }

      public int ViewHeight
      {
         get { return viewHeight; }
         set
         {
            if (viewHeight != value)
            {
               viewHeight = value;
               isProjectionDirty = true;
            }
         }
      }

      public Quaternion Rotation
      {
         get { return rotation; }
         set
         {
            if (rotation != value)
            {
               isViewDirty = true;

               rotation = value;

               var rotMat = Matrix.RotationQuaternion(rotation);

               this.up = Vector3.TransformNormal(Vector3.UnitY, rotMat);
               this.forward = Vector3.TransformNormal(Vector3.UnitZ, rotMat);
               this.left = Vector3.TransformNormal(Vector3.UnitX, rotMat);

               if (isEyeAtPosition)
               {
                  lookAt = position + viewDistance * forward;
               }
               else
               {
                  eye = position - viewDistance * forward;
               }
            }
         }
      }

      public Vector3 Position
      {
         get { return position; }
         set
         {
            if (position != value)
            {
               isViewDirty = true;

               position = value;

               if (isEyeAtPosition)
               {
                  eye = position;
                  lookAt = position + viewDistance * forward;
               }
               else
               {
                  lookAt = position;
                  eye = position - viewDistance * forward;
               }
            }
         }
      }

      public float ViewDistance
      {
         get { return viewDistance; }
         set
         {
            if (viewDistance != value)
            {
               isViewDirty = true;
               viewDistance = value;

               if (isEyeAtPosition)
               {
                  lookAt = position + viewDistance * forward;
               }
               else
               {
                  eye = position - viewDistance * forward;
               }
            }
         }
      }

      public Vector3 Eye
      {
         get { return eye; }
      }

      public Vector3 LookAt
      {
         get { return lookAt; }
      }

      public Vector3 Up
      {
         get { return up; }
      }

      public Vector3 Forward
      {
         get { return forward; }
      }

      public Vector3 Left
      {
         get { return left; }
      }

      public Matrix View
      {
         get
         {
            if (isViewDirty)
            {
               view = Matrix.LookAtLH(eye, lookAt, up);
               isViewDirty = false;
            }
            return view;
         }
      }

      public Matrix Projection
      {
         get
         {
            if (isProjectionDirty)
            {
               projection = Matrix.PerspectiveFovLH((float)Math.PI / 2.0f, (float)viewWidth / (float)viewHeight, 1.0f, 100.0f);
               isProjectionDirty = false;
            }
            return projection;
         }
      }

      #endregion

      #region Methods

      #endregion
   }
}
